﻿using System;
using System.Net;
using System.Numerics;
using System.Security.Cryptography;
using System.Web;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore.Metadata.Internal;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using up6.db.biz;
using up6.db.database;
using up6.db.database.down2.sql;
using up6.db.database.up6.sql;
using up6.db.filemgr;
using up6.db.model;
using up6.db.sql;
using up6.filemgr.app;
using up6.sql;
using up6.utils;
using Up6.core.Up6Manager.Biz;
using Up6.core.Up6Manager.Models.Req;

namespace Up6.core.Up6Manager;

public class MgrAction
{    
    public IActionResult Data(FileInf f)
    {        
        if (!f.pathRel.EndsWith("/")) f.pathRel += "/";        
        var m_pathRelCds = string.Format("f_pathRel='{0}'+f_nameLoc", f.pathRel);

        var tp = ConfigReader.dbType();
        if (tp == DataBaseType.Kingbase || tp == DataBaseType.ODBC || tp == DataBaseType.Oracle)
        {
            m_pathRelCds = string.Format("f_pathRel=CONCAT('{0}',f_nameLoc)", f.pathRel);
        }
        if (f.pathRel == "/")
        {
            var fs = SqlTable.build("up6_files").reads<FileInf>(
            "f_id,f_pid,f_nameLoc,f_sizeLoc,f_lenLoc,f_time,f_pidRoot,f_fdTask,f_pathSvr,f_pathRel,f_lenLocSec,f_encrypt,f_encryptAgo,f_blockSize,f_blockSizeSec,f_object_key",
            SqlWhere.build()
            .eq("f_complete", true)
            .eq("f_uid", f.uid)
            .eq("f_deleted", false)
            .eq("f_fdChild", false)
            .sql("f_pathRel", m_pathRelCds),
            SqlSort.build().desc("f_fdTask").desc("f_time")
            );

            JObject o = new JObject();
            o["count"] = fs.Count();
            o["code"] = 0;
            o["msg"] = string.Empty;
            o["data"] = JArray.FromObject(fs);

            return new ContentResult
            {
                Content = string.Format("{0}", JsonConvert.SerializeObject(o)),
                ContentType = "application/json; charset=utf-8",
            };
        }
        else 
        {
            var files = SqlTable.build("up6_files").reads<FileInf>(
                "f_id,f_pid,f_nameLoc,f_sizeLoc,f_lenLoc,f_time,f_pidRoot,f_fdTask,f_pathSvr,f_pathRel,f_lenLocSec,f_encrypt,f_encryptAgo,f_blockSize,f_blockSizeSec,f_object_key",
                SqlWhere.build()
                .eq("f_uid", f.uid)
                .eq("f_complete", true)
                .eq("f_deleted", false)
                .eq("f_fdChild", true)
                .sql("f_pathRel", m_pathRelCds),
                SqlSort.build()
                .desc("f_fdTask")
                .desc("f_time").asc("f_nameLoc"));

            var folders = SqlTable.build("up6_folders").reads<FileInf>(
                "f_id,f_nameLoc,f_pid,f_sizeLoc,f_time,f_pidRoot,f_pathRel",
                SqlWhere.build()
                .eq("f_complete", true)
                .eq("f_deleted", false)
                .eq("f_uid", f.uid)
                .sql("f_pathRel", m_pathRelCds),
                SqlSort.build()
                .desc("f_time")
                .asc("f_nameLoc"));

            foreach (var fd in folders)
            {
                fd.fdTask = true;
                fd.fdChild = false;
                fd.pathSvr = string.Empty;
                files.Insert(0, fd);
            }

            JObject o = new JObject();
            o["count"] = files.Count();
            o["code"] = 0;
            o["msg"] = string.Empty;
            o["data"] = JArray.FromObject(files);

            return new ContentResult
            {
                Content = string.Format("{0}", JsonConvert.SerializeObject(o)),
                ContentType = "application/json; charset=utf-8",
            };
        }
    }

    /// <summary>
    /// 文件初始化
    /// </summary>
    /// <param name="req"></param>
    /// <returns></returns>
    public async Task<IActionResult> CreateAsync(FileInf f)
    {
        //参数为空
        if (
            string.IsNullOrEmpty(f.md5)
            || string.IsNullOrEmpty(f.uid)
            || string.IsNullOrEmpty(f.sizeLoc)
        )
        {
            return new ContentResult { 
                Content= f.callback + "({\"value\":null,\"ret\":false,\"error\":\"参数为空\"})",
                ContentType= "application/json; charset=utf-8",
                StatusCode =200
            };
        }

        if (!ConfigReader.inFileExts(f.pathLoc))
        {
            string m = f.callback + "({\"value\":\"0\",\"ret\":false,\"error\":\"文件类型非法\"})";//返回jsonp格式数据。

            return new ContentResult
            {
                Content = m,
                ContentType = "application/json; charset=utf-8",
                StatusCode = 200
            };
        }        

        PathBuilderUuid pb = new PathBuilderUuid();
        f.pathSvr = pb.genFile(f.uid,f );

        //检查相同文件
        var sql = up6.db.database.up6.sql.SqlFile.build();
        var fsvr = sql.exist_file(f.md5);
        if (fsvr != null) {
            f.nameSvr = fsvr.nameSvr;
            f.pathSvr = fsvr.pathSvr;
            f.pathRel = fsvr.pathRel;
            f.perSvr = fsvr.perSvr;
            f.lenSvr = fsvr.lenSvr;
            f.complete  = fsvr.complete;
            f.lenLocSec = fsvr.lenLocSec;
            f.encryptAgo = fsvr.encryptAgo;
            f.encrypt = fsvr.encrypt;
            f.blockSize = fsvr.blockSize;
            f.blockSizeSec = fsvr.blockSizeSec;
            f.object_key = fsvr.object_key;
            sql.Add(f);
        }//数据库不存在相同文件
        else {
            //创建文件
            var fw = ConfigReader.blockWriter();
            try
            {
                f.object_id = await fw.makeAsync(f);
                f.object_key = f.getObjectKey();
            }
            catch (IOException ie)
            {
                return new ContentResult
                {
                    Content = f.callback+string.Format("(\"value\":\"\",\"error\":\"{0}\",\"ret\":false)",ie.Message),
                    ContentType = "application/json; charset=utf-8",
                    StatusCode = 200
                };
            }

            sql.Add(f);
        }        

        string jv = JsonConvert.SerializeObject(f);
        jv = HttpUtility.UrlEncode(jv);
        jv = jv.Replace("+", "%20");
        string json = f.callback + "({\"value\":\"" + jv + "\",\"ret\":true})";//返回jsonp格式数据。

        return new ContentResult
        {
            Content = json,
            ContentType = "application/json; charset=utf-8",
            StatusCode = 200
        };
    }

    public IActionResult complete(FileInf f)
    {
        int ret = 0;
        if (!string.IsNullOrEmpty(f.id))
        {
            var db = up6.db.database.up6.sql.SqlFile.build();
            db.complete(f.id);
            var file = db.read(f.id);

            //覆盖同名文件-更新同名文件状态
            if (f.cover == 1) db.delete(file.pathRel, f.uid, f.id);

            ret = 1;
        }
        return new ContentResult
        {
            Content = f.callback+"("+ret+")",
            ContentType = "application/json; charset=utf-8",
            StatusCode = 200
        };
    }
    public IActionResult del(FileInf f)
    {
        f.pathRel += '/';

        SqlWhereMerge swm = new SqlWhereMerge();
        DBConfig cfg = new DBConfig();
        var tp = ConfigReader.dbType();
        if (tp == DataBaseType.Kingbase || tp == DataBaseType.ODBC || tp == DataBaseType.Oracle)
        {
            swm.instr(f.pathRel, "f_pathRel");
        }
        else
        {
            swm.charindex(f.pathRel, "f_pathRel");
        }
        string where = swm.to_sql();

        SqlExec se = cfg.se();
        se.update("up6_folders"
            , new SqlParam[] { new SqlParam("f_deleted", true) }
            , where
            );

        se.update("up6_folders"
            , new SqlParam[] { new SqlParam("f_deleted", true) }
            , new SqlParam[] { new SqlParam("f_id", f.id) }
            );

        se.update("up6_files"
            , new SqlParam[] { new SqlParam("f_deleted", true) }
            , where
            );

        se.update("up6_files"
            , new SqlParam[] { new SqlParam("f_deleted", true) }
            , new SqlParam[] { new SqlParam("f_id", f.id) }
            );

        JObject o = new JObject { { "ret", 1 } };


        return new ContentResult
        {            
            Content = string.Format("{0}", JsonConvert.SerializeObject(o)),
            ContentType = "application/json; charset=utf-8",            
        };
    }

    /// <summary>
    /// 文件夹初始化
    /// </summary>
    /// <param name="f"></param>
    /// <returns></returns>
    public IActionResult fd_create(FileInf f)
    {
        string pathRel = f.pathRel;
        f.pathRel = PathTool.combin(f.pathRel, f.nameLoc);

        //生成路径
        PathBuilderUuid pb = new PathBuilderUuid();
        f.pathSvr = pb.genFolder(f);
        //创建文件夹错误
        if(!PathTool.mkdir(f.pathSvr))
        {
            var js = new JObject { { "msg", "create dir error" } };

            return new ContentResult
            {
                Content = f.callback + string.Format("({0})",JsonConvert.SerializeObject(js)),
                ContentType = "application/json; charset=utf-8",
                StatusCode = 500
            };
        }

        //创建层级信息文件
        FolderSchema fs = new FolderSchema();
        if (!fs.create(f))
        {
            var js = new JObject { { "msg", "create schema file error" } };
            return new ContentResult
            {
                Content = f.callback + string.Format("({0})",JsonConvert.SerializeObject(js)),
                ContentType = "application/json; charset=utf-8",
                StatusCode = 500
            };
        }

        //添加成根目录
        if (pathRel == "/")
        {            
            up6.db.database.up6.sql.SqlFile.build().Add(f);
        }//添加成子目录
        else
        {
            SqlFolder.build().add(f);
        }
        
        var json = JsonConvert.SerializeObject(f);
        json = WebUtility.UrlEncode(json);
        json = json.Replace("+", "%20");        
        var jo = new JObject { { "value", json }, { "ret", true } };

        return new ContentResult
        {
            Content = f.callback + string.Format("({0})",JsonConvert.SerializeObject(jo)),
            ContentType = "application/json; charset=utf-8",
        };
    }

    public IActionResult fd_complete(FileInf f)
    {
        int ret = 0;
        if(!string.IsNullOrEmpty(f.id))
        {
            FileInf folder = null;
            FileInf fdExist = null;

            //当前目录是根目录?
            bool isRootDir = f.pathRel == "/";

            //根目录
            if (isRootDir)
            {
                folder = up6.db.database.up6.sql.SqlFile.build().read(f.id);
                //查询同级同名目录
                fdExist = up6.db.database.up6.sql.SqlFile.build().read(folder.pathRel, f.id, folder.uid);

                //存在相同目录=>直接使用已存在的目录信息=>删除旧目录
                if (fdExist != null) up6.db.database.up6.sql.SqlFile.build().Delete(f.uid, fdExist.id);
            }
            //子目录
            else
            {
                folder = SqlFolder.build().read(f.id);
                folder.uid = f.uid;
                fdExist = SqlFolder.build().read(folder.pathRel, f.id, folder.uid);

                //存在相同目录=>直接使用已存在的目录信息=>删除旧目录
                if (fdExist != null) SqlFolder.build().del(fdExist.id, f.uid);
            }

            //存在同名，同路径文件
            if (fdExist != null)
            {
                folder.id = fdExist.id;
                folder.pid = fdExist.pid;
                folder.pidRoot = fdExist.pidRoot;
            }

            //根节点
            FileInf root = new FileInf();
            root.id = folder.pidRoot;
            root.uid = folder.uid;
            //当前节点是根节点
            if (string.IsNullOrEmpty(root.id)) root.id = folder.id;

            //保存层级结构-解析层级信息文件
            FolderScaner fsd = new FolderScaner();
            fsd.m_cover = fdExist != null;
            fsd.save(folder);

            //上传完毕=>子目录
            if (!isRootDir) SqlFolder.build().complete(f.id, f.uid);
            //根目录
            else up6.db.database.up6.sql.SqlFile.build().complete(f.id);            

            ret = 1;
        }

        return new ContentResult
        {
            Content = f.callback + "(" + ret + ")",
            ContentType = "application/json; charset=utf-8",
        };
    }

    public IActionResult fd_data(FileInf f)
    {
        string json = f.callback + "({\"value\":null})";
        if (!string.IsNullOrEmpty(f.id))
        {
            FolderBuilder fb = new FolderBuilder();
            var data = JsonConvert.SerializeObject(fb.build(f.id, f.pid));            
            data = WebUtility.UrlEncode(data);
            data = data.Replace("+", "%20");

            json = f.callback + "({\"value\":\"" + data + "\"})";
        }

        return new ContentResult
        {
            Content = json,
            ContentType = "application/json; charset=utf-8",
        };        
    }

    public IActionResult mkdir(FileInf f)
    {
        bool isRootDir = f.pathRel == "/";
        f.pathRel = PathTool.combin(f.pathRel, f.nameLoc);

        DbFolder df = new DbFolder();
        if (df.exist_same_folder(f.pathRel))
        {            
            return new ContentResult
            {
                Content = "({\"ret\":false,\"msg\":\"已存在同名目录\"})",
                ContentType = "application/json; charset=utf-8",
            };
        }

        f.id = Guid.NewGuid().ToString("N");
        f.complete = true;
        f.fdTask = true;

        //根目录
        if (isRootDir)
        {
            SqlTable.build("up6_files").insert(f);
        }//子目录
        else
        {
            SqlTable.build("up6_folders").insert(f);
        }

        JObject obj = JObject.FromObject(f);
        obj["ret"] = true;

        return new ContentResult
        {
            Content = string.Format("{0}", JsonConvert.SerializeObject(obj)),
            ContentType = "application/json; charset=utf-8",
        };
    }

    public IActionResult path(string data)
    {
        data = WebUtility.UrlDecode(data);
        var fd = JObject.Parse(data);

        DbFolder df = new DbFolder();
        var d = df.build_path(fd);

        return new ContentResult
        {
            Content = string.Format("{0}", JsonConvert.SerializeObject(JToken.FromObject(d))),
            ContentType = "application/json; charset=utf-8",
        };
    }

    public IActionResult rename(FileInf f)
    {
        //当前目录是根目录?
        bool isRootDir = PathTool.parentDir(f.pathRel) == "/";

        if (!f.fdTask)
        {
            //新的相对路径
            f.pathRel = Path.Combine(PathTool.parentDir(f.pathRel), f.nameLoc);
            f.pathRel = f.pathRel.Replace('\\', '/');

            //根据相对路径(新)=>查找同名文件
            var s = new FileInf();
            s = SqlTable.build("up6_files").readOne<FileInf>(
                //相对路径=>/dir
                SqlWhere.build()
                .eq("f_pathRel", f.pathRel)
                .eq("f_deleted", false)
                );

            bool exist = s != null;

            //不存在同名文件
            if (!exist)
            {
                SqlTable.build("up6_files").update(
                    SqlSeter.build()
                    .set("f_nameLoc", f.nameLoc)
                    .set("f_nameSvr", f.nameLoc)
                    .set("f_pathRel", f.pathRel),
                    SqlWhere.build().eq("f_id", f.id)
                    );

                var ret = new JObject { { "state", true },
                    { "pathRel",f.pathRel} };
                
                return new ContentResult
                {
                    Content = string.Format("{0}", JsonConvert.SerializeObject(ret)),
                    ContentType = "application/json; charset=utf-8",
                };
            }
            //存在同名项
            else
            {
                var res = new JObject { { "state", false }, { "msg", "存在同名项" } };

                return new ContentResult
                {
                    Content = string.Format("{0}", JsonConvert.SerializeObject(res)),
                    ContentType = "application/json; charset=utf-8",
                };
            }
        }
        //是子目录
        else if (!isRootDir)
        {
            var pathRelOld = f.pathRel;
            //root/dir/name => root/dir
            var index = f.pathRel.LastIndexOf("/");
            f.pathRel = f.pathRel.Substring(0, index + 1);
            //root/dir/old => root/dir/new
            f.pathRel += f.nameLoc;
            var pathRelNew = f.pathRel;

            var fd = SqlTable.build("up6_folders").readOne<FileInf>("f_id,f_pathRel",
                SqlWhere.build()
                .eq("f_pathRel", pathRelNew)
                .eq("f_deleted", false)
                );
            bool exist = fd != null;

            //不存在同名目录
            if (!exist)
            {
                //更新相对路径
                SqlTable.build("up6_folders").update(
                    SqlSeter.build()
                    .set("f_nameLoc", f.nameLoc)
                    .set("f_pathRel", pathRelNew),
                    SqlWhere.build().eq("f_id", f.id)
                    );

                //更新子级文件和目录路径
                this.folder_renamed(pathRelOld, pathRelNew);
                var ret = new JObject {
                    { "state", true },
                    { "pathRel",pathRelNew}
                };
                
                return new ContentResult
                {
                    Content = string.Format("{0}", JsonConvert.SerializeObject(ret)),
                    ContentType = "application/json; charset=utf-8",
                };
            }
            //存在同名项
            else
            {
                var res = new JObject { { "state", false }, { "msg", "存在同名项" } };
                
                return new ContentResult
                {
                    Content = string.Format("{0}", JsonConvert.SerializeObject(res)),
                    ContentType = "application/json; charset=utf-8",
                };
            }
        }
        //是根目录
        else
        {
            //根据相对路径(新)=>查找同名文件夹
            var pathRel = "/" + f.nameLoc;
            var s = SqlTable.build("up6_files").readOne<FileInf>(
                //相对路径=>/dir
                SqlWhere.build()
                .eq("f_pathRel", pathRel)
                .eq("f_deleted", false)
                );

            bool exist = s != null;

            //不存在同名目录
            if (!exist)
            {
                //查找目录旧的相对路径
                s = SqlTable.build("up6_files").readOne<FileInf>(
                    "f_pathRel",
                    SqlWhere.build().eq("f_id", f.id));

                SqlTable.build("up6_files").update(
                    SqlSeter.build()
                    .set("f_nameLoc", f.nameLoc)
                    .set("f_nameSvr", f.nameLoc)
                    .set("f_pathRel", pathRel),
                    SqlWhere.build().eq("f_id", f.id)
                    );

                //更新子级文件和目录路径
                this.folder_renamed(s.pathRel, pathRel);

                var ret = new JObject { { "state", true },
                    { "pathRel",pathRel} };
                
                return new ContentResult
                {
                    Content = string.Format("{0}", JsonConvert.SerializeObject(ret)),
                    ContentType = "application/json; charset=utf-8",
                };
            }
            //存在同名项
            else
            {
                var res = new JObject { { "state", false }, { "msg", "存在同名项" } };
                
                return new ContentResult
                {
                    Content = string.Format("{0}", JsonConvert.SerializeObject(res)),
                    ContentType = "application/json; charset=utf-8",
                };
            }
        }
    }

    public IActionResult search(FileInf f)
    {
        if (!f.pathRel.EndsWith("/")) f.pathRel += '/';
        bool isRootDir = f.pathRel == "/";

        string pathRelSql = string.Format("f_pathRel='{0}'+f_nameLoc", f.pathRel);
        string keySql = string.Format("f_nameLoc like '%{0}%'", f.key);

        var tp = ConfigReader.dbType();
        if (tp == DataBaseType.Oracle || tp == DataBaseType.ODBC || tp == DataBaseType.Kingbase)
        {
            pathRelSql = string.Format("f_pathRel=CONCAT('{0}',f_nameLoc)", f.pathRel);
            keySql = string.Format("f_nameLoc like '%%{0}%%'", f.key);
        }
        if (isRootDir) pathRelSql = "";

        //文件表
        var files = SqlTable.build("up6_files").reads<FileInf>(
            "f_id,f_pid,f_nameLoc,f_sizeLoc,f_lenLoc,f_time,f_pidRoot,f_fdTask,f_pathSvr,f_pathRel,f_lenLocSec,f_encrypt,f_encryptAgo,f_blockSize,f_blockSizeSec,f_object_key",
            SqlWhere.build()
            .eq("f_complete", true)
            .eq("f_deleted", false)
            .eq("f_uid", f.uid)
            .sql("f_pathRel", pathRelSql)
            .sql("f_nameLoc", keySql),
            SqlSort.build().desc("f_fdTask").desc("f_time"));

        //目录表
        var folders = SqlTable.build("up6_folders").reads<FileInf>(
            "f_id,f_nameLoc,f_pid,f_sizeLoc,f_time,f_pidRoot,f_pathRel",
            SqlWhere.build()
            .eq("f_complete", true)
            .eq("f_deleted", false)
            .eq("f_uid", f.uid)
            .sql("f_pathRel", pathRelSql)
            .sql("f_nameLoc", keySql),
            SqlSort.build().desc("f_time"));

        foreach (var fd in folders)
        {
            fd.fdTask = true;
            fd.fdChild = false;
            files.Add(fd);
        }

        JObject o = new JObject();
        o["count"] = folders.Count;
        o["code"] = 0;
        o["msg"] = string.Empty;
        o["data"] = JToken.FromObject(files);

        return new ContentResult
        {
            Content = string.Format("{0}", JsonConvert.SerializeObject(o)),
            ContentType = "application/json; charset=utf-8",
        };
    }

    public IActionResult uncmp_down(FileInf f)
    {
        var fs = SqlTable.build("down_files").reads<DnFileInf>(
                "f_id,f_nameLoc,f_pathLoc,f_perLoc,f_sizeSvr,f_fdTask",
                SqlWhere.build().eq("f_uid", f.uid),
                SqlSort.build());        

        return new ContentResult
        {
            Content = string.Format("{0}", JsonConvert.SerializeObject(JToken.FromObject(fs))),
            ContentType = "application/json; charset=utf-8",
        };
    }

    public IActionResult uncomp(FileInf f)
    {
        var files = SqlTable.build("up6_files").reads<FileInf>(
                "f_id ,f_nameLoc ,f_pathLoc ,f_sizeLoc ,f_lenSvr ,f_perSvr ,f_fdTask ,f_md5 ",
                SqlWhere.build()
                .eq("f_complete", false)
                .eq("f_deleted", false)
                .eq("f_uid", f.uid),
                SqlSort.build());

        return new ContentResult
        {
            Content = string.Format("{0}", JsonConvert.SerializeObject(JToken.FromObject(files))),
            ContentType = "application/json; charset=utf-8",
        };
    }

    /// <summary>
    /// 目录名称更新，
    /// 1.更新数据表中所有子级文件相对路径
    /// 2.更新数据表中所有子级目录相对路径
    /// </summary>
    /// <param name=""></param>
    void folder_renamed(string pathRelOld, string pathRelNew)
    {
        DBConfig cfg = new DBConfig();
        SqlExec se = cfg.se();
        string sql = string.Format("update up6_files set f_pathRel=REPLACE(f_pathRel,'{0}/','{1}/') where CHARINDEX('{0}/',f_pathRel)=1",
            pathRelOld,
            pathRelNew
            );
        if (ConfigReader.dbType() == DataBaseType.Oracle)
        {
            sql = string.Format("update up6_files set f_pathRel=REPLACE(f_pathRel,'{0}/','{1}/') where instr(f_pathRel,'{0}/')=1",
            pathRelOld,
            pathRelNew
            );
        }
        se.exec(sql);

        //更新目录表
        sql = string.Format("update up6_folders set f_pathRel=REPLACE(f_pathRel,'{0}/','{1}/') where CHARINDEX('{0}/',f_pathRel)=1",
            pathRelOld,
            pathRelNew
            );
        if (ConfigReader.dbType() == DataBaseType.Oracle)
        {
            sql = string.Format("update up6_folders set f_pathRel=REPLACE(f_pathRel,'{0}/','{1}/') where instr(f_pathRel,'{0}/')=1",
            pathRelOld,
            pathRelNew
            );
        }
        se.exec(sql);
    }
}
